home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The CICA Windows Explosion!
/
The CICA Windows Explosion! - Disc 2.iso
/
programr
/
genmix1b.zip
/
GENMIX
/
README.TXT
Wrap
Text File
|
1995-04-15
|
23KB
|
547 lines
Thanks for requesting information about Genmix! And, thanks for being
patient while I got this stuff together. The first beta will be relased
next week, and another message will be sent to this list telling ya where
to download it (on compuserve and the internet). Here's the release notes
for the first beta, they should answer most of your questions. If you have
any questions not covered in these notes, please mail them to me, and I'll
add what answers I can to the first distribution.
-Ian
---------------------------------------------------------------------------
GENMIX BETA 1 RELEASE NOTES
WHAT IS GENMIX
--------------
Genmix is a C code library that you can add to your MS Windows or Macintosh
applications. Genmix provides real-time additive waveform synthesis and
output -- it mixes and plays sounds. A 17-function API (Application Program
Interface) allows full control over the sound mixing process.
BETA NOTICE
-----------
This software is currently not ready to be incorporated in commercial
applications. There are problems both with the generic mixer code, and with
the implementations of wave i/o. See the section "current problems" for
more information. So, use this code AT YOUR OWN RISK.
Genmix will remain in beta until it is stable and runs well on 95% installed
Macintosh and MPC2 base. There will always be a few percent with obsolete
or unsupported operating systems, hardware, or software audio drivers.
Sound quality should be as good as possible on systems supported.
Beta is expected to end shortly _after_ Windows 95 ships, in 4Q95. Expect
probably four or five beta releases.
LICENSE AGREEMENT
-----------------
Genmix is (c) Copyright 1995 Ian A. McLean. All rights are reserved except
as described in the following four items:
1. You may redistribute the ZIP file containing this
file, but only in its original, unmodified form.
No fee may be charged.
2. You may NOT distribute any application derived from
any portion of the Genmix source code without express
written permission from Ian A. McLean.
3. You may create works derived from the Genmix source
code only if they add significant and primary
functionality to the Genmix source code.
4. You are granted a non-exclusive right to use and
modify the Genmix source code for the sole purposes
of designing, developing, and testing applications
derived from the Genmix source code.
PLEASE use Genmix in your applications! Licenses for
redistribution of derived works are available according to the following
price schedule:
License to distribute derived works:
500 copies or less.............. $FREE
First 20,000 copies............. $500
Each additional 20,000 copies... $1000
This pricing information is subject to change without notice. If you're a
public domain or shareware author, or you feel this agreement isn't fair,
please let me know; I'll be more than happy to talk about working out
alternate arrangements.
CONTACTING ME
-------------
Currently, there is no public forum for supporting Genmix. I usually lurk
on the internet at comp.os.ms-windows.programmer.multimedia, and on
compu$erve at GO:GAMEDEV Windows Games. I'll send information to
those newsgroups, probably, as well as to users on the beta mailing list.
Please contact me if you have questions about Genmix, bug reports (I like
those), suggestions, questions about licensing, general advice, good
jokes, or any other reason to chat.
You can send email to me at: ianm@usit.net
Snail-mail can reach me at:
Ian McLean
916 Gertrude Ave.
Knoxville TN 37920
Please contact me by email rather than telephone.
DISTRIBUTION CONTENTS
---------------------
Genmix is provided as a ZIP file, expanding to the following directory
structure and files:
/genmix/readme.txt This file
/genmix/bin/portwave.ini Mixer initialization file
/genmix/bin/mixtest.exe Mixer test program executable
/genmix/bin/mixtestd.exe Debug mixer test program executable
/genmix/src/genmix.c Mixer source code
/genmix/src/port.h Abstraction layer header
/genmix/src/portmem.c Memory abstraction layer
/genmix/src/portwave.c Sound hardware abstraction layer
/genmix/tst/mixtest.c Mixer test program source code
/genmix/tst/mixtest.rc Test program resources
/genmix/tst/mixtest.mak Mixer test program make file
/genmix/tst/mixtest.vcp Some file visual c makes
CURRENT PROBLEMS/ISSUES
-----------------------
Sorry! Macintosh sound code is not included in this first beta release
of Genmix. It will be added as soon as I have time to rewrite the routines
to provide a variable size ring buffer.
The file PORTWAVE.INI should be copied to your Windows directory. This file
stores the default hardware configuration data and information specific to
your configuration. PLEASE fool around with the "Hardware Config..."
options in the test program, and tune them for best results on your
configuration. THEN, send me a copy of your PORTWAVE.INI file. This will
let me make future releases work better, with your tweaks applied.
The best method to tweak the setting for a certain format is:
-Set number of blocks to 4
-Set block size to 16384
-Set remix to at end of block
-Set getpos to time ourselves
-Play a long sound, 5-10 seconds
-Lower block size, choosing smallest that sounds ok
-Lower number of blocks, going no less than two
-Play a short sound, <1 sec, in a loop
-Change remix to immediate
-If you get hisses or pops, note this in a bug report
If setting the block size too low crashes your system, copying the file
portwave.bak over portwave.ini will undo the last configuration change
you made. If you don't undo this change, the mixer test program will crash
again as soon as its loaded.
The test program is not really a very good example program. It's sloppy,
confusing, and probably buggy.
The Windows abstraction layer has several serious problem -- some of them
Microsoft's fault, most the fault of individual audio driver manufacturers.
I use the waveOut API to drive the sound. The waveOut API has some flaws,
mostly because the requirements for a good quality driver aren't clearly spelt
out. There is no function available to halt currently playing sounds without
perhaps resetting the DAC on the sound card. Many audio manufacturers have
shipped buggy drivers with their products. Here's some gotchas about using
the waveOut API:
- waveOutReset can take up to 1.5 seconds to complete, and it can cause
audible clicking or popping. This happens on SB2.0's and NEC98's.
- waveOutWrite callbacks don't occur at the correct time unless the
size of the block played is a multiple of the dma block size. The dma
block size is 2k,4k,8k, or 16k, depending upon the device and version
of Windows.
- waveOutGetPos doesn't return an accurate count of samples played. The
amount of inaccuracy varies from sound card to sound card.
- the sound output rate can be 5% faster or slower than stated
Due to these and several other problems, it is my opinion that
IT IS IMPOSSIBLE TO IMPLEMENT HIGH QUALITY REAL-TIME AUDIO
USING THE WAVEOUT API
and Microsoft should get up and do something about it. Maybe they will --
as I write this, Manhattan is still undisclosed; maybe the mysterious
DirectSound API will address this problem. I hope so!
There are several bugs still lurking in the generic mixer code. Due to a
last minute spec change, I had to rewrite much of the data rendering code
to allow for installable codecs to be added in a future release. This
introduced several new bugs into previously well-tested areas of the code.
I found most of them, but I haven't had time to test the code as rigorously
as I would like. Keep your eyes peeled for 'em.
I _swear_ the mixer test program is haunted. It seems to lock up for no
reason at all on some configurations, notably Sound Blaster v2.0, when
closing and reopening the mixer. I think this might be a winmm bug, but
it need further investigation. Sound Blaster 2.0 users, please let me know
how the mixer performs. Run the test program in an environment with debug
capabilities, and set a breakpoint on portFatalError().
Many sound cards have noticeable clicks and pops when remixing. Please let
me know what your card sounds like when you send me PORTWAVE.INI information.
And, of course, many sound card drivers Just Plain Crash if you give them
too complex a task. I won't name names right now, but just be sure you
have the latest version of your sound card driver, and report your driver
and version along with any bug reports.
mixPause() and mixResume() aren't implemented yet. Well, they're implemented
but I haven't actually tested them to see if they work in all cases.
mixInstallCodec() and the whole on-the-fly decompression thing
isn't implemented. I've no plans to implement any codecs for release,
they're won't be useful unless you want to write your own. I _might_ be
convinced to write an ADPCM codec, but only if it's not too much of a hassle.
If anyone has any good ADPCM compression / decompression source, I'd really
like to see it.
I've had several people suggest I come up with a better technique to fill
the sound output queue than calling mixPump(). Well, I agree, something
like that should be done automatically by a parallel thread. However, I need
this mixer to be able to run under Win31, and Win31 doesn't support any kind
of preemptive multitasking for 32 bit programs. Under Win32 (95 or NT), you
could create a thread that does nothing but mixPump() when needed, and I
_think_ there won't be any synchronization problems, but I haven't tested it
yet. Now, it _is_ possible to get a interrupt-level timer in Win32s, but
waveOutWrite() should never be called from a timer interrupt (certain parts of
winmm are discardable, if they're not in memory and you try to access them,
you'll page fault. Page faulting during a timer interrupt results in a
nasty crash). Don't get hung up on Microsoft's push-pull event architecture,
it's really kind of anal retentive. See the mixer test main loop for a way
to avoid having to send yourself timer messages. CD-ROM authors, be sure
to write an abstraction layer for file i/o that breaks long reads up into
chunks, pumping between chunks. I'm using mmio for file access, and haven't
had a problem with the chunking slowing the transfer rate. This will prevent
dropout during long reads.
This version has not been tested on many Win32s configurations, and it hasn't
been tested at all under NT. I don't see any reason why it wouldn't work
under NT, please let me know what your experiences are.
MIXER DATA STRUCTURES
---------------------
Genmix uses only two public data structures.
The mixVol structure describes a volume setting, for left and right channels
of a sound. The mixSound structure represents a sound sample:
typedef struct tagmixSound
{
mixCodecType type;
pmem pdata;
long samples;
waveRate rate;
waveBits bits;
waveSpeak speak;
mixVol vol;
} mixSound;
where:
type should be set to mixCodecPCM
pdata is a LPSTR to the wave data
samples counts the samples in the sound
rate is waveRate11k, waveRate22k, or waveRate44k
bits is either waveBits8 or waveBits16
speak is either waveSpeakMono or waveSpeakStereo
vol describes the volume the sound will be played at
MIXER API
---------
The following 17 functions implement the Genmix API. Sorry for the
scant description.
rescode mixOpen(short numchan, waveRate rate, waveBits bits,
waveSpeak speak);
Opens the sound hardware and prepares to mix sounds. Up to (numchan)
sounds can be mixed at once. (rate), (bits), and (speak) describe
the format of the output waveform.
rescode mixClose(void);
Closes the mixer and associate sound hardware. All playback stops.
rescode mixPlay(short chan, pmixSound psound);
Plays the sound sample specified by (psound) on mixer channel (chan).
If a sound is currently playing on channel (chan), it is truncated and
(psound) starts playing immediatly.
rescode mixUnplay(pmem pdata);
Stops the data (pdata) from playing, if it is currently in use by the
mixer. This is usually called as a safeguard before disposing of
(pdata).
rescode mixAppend(short chan, pmixSound psound)
Plays the sound sample (psound) on mixer channel (chan). If a sound
is currently playing on (chan), it continues playing, and (psound) is
played after all previous sounds have finished. If (chan) is silence,
(psound) starts playing immediatly.
rescode mixSilence(short chan);
Immediatly silences channel (chan), if any sounds are playing.
rescode mixIsSilent(short chan);
Returns non-zero if channel (chan) is silence, eg no sounds are playing.
rescode mixLoadChannel(short, pmixSound *, short);
Not implemented yet. Will implement theme music loops.
rescode mixSetVolume(short chan, pmixVol pvol);
Sets the volume of channel (chan) to (pvol). The output volume is
a combination of the sample volume and the volume of the channel it
is playing on.
rescode mixGetVolume(short chan, pmixVol pvol);
Gets the volume of channel (chan), storing it in (pvol).
rescode mixIsDataPlaying(pmem pdata);
Returns non-zero if the data (pdata) is currently in use by the mixer.
rescode mixPause(void);
Not implemented yet.
rescode mixResume(void);
Not implemented yet.
rescode mixPump(void);
This function maintains the flow of waveform data to the sound hardware.
It must be called from the application program at least every 1/10th of
a second.
rescode mixVolCreate(pmixVol pvol, double dleft, double dright);
Utility function to create a volume specifier in (pvol). (dleft) and
(dright) specify the volume of the left and right output speakers.
0.0 is silent, 1.0 is full volume, greater than 1.0 increases the
input amplitude.
rescode mixVolMult(pmixVol pv1, pmixVol pv2, pmixVol pvdest);
Utility function to combine two volume specifiers. (pv1) is multiplied
with (pv2) and the result is placed in (pvdest)
rescode mixInstallCodec(pmixCodec);
Not implemented yet.
MIXER ARCHITECTURE
------------------
Genmix is very object oriented C code (not C++!). To explain the different
objects involved, let's trace the course of an audio sample from it's
source, through the mixer, and out the speakers. The path takes us through
four different objects:
DATA SOURCE (Hungarian notation: dsrc)
A data source object is a source of waveform data. Its primary function is
to convert waveform data from one format (e.g., 22kHz, 8bit, mono) to another
format (e.g., 44kHz, 16bit, stereo), a task called rendering. A data source
has the following methods:
Render -- render a specified number of samples to a destination
buffer, advancing the data source stream.
Create -- create either a source of silence or a sample stream
Destroy
Rewind -- rewinds the data source stream
GetPos -- gets the current data source stream pointer
Truncate -- truncates the data source at the current position
Basically, a data source is just an abstraction for an uncompressed PCM
stream. The data source object could be tightly bound to the mixer channel
without things getting too complex, but I've separated it out to allow for
easy implementation of installable codecs in the future.
MIXER CHANNEL (Hungarian notation: chan)
A mixer channel is a doubly-linked list of data sources. It represents a
temporal cross section of the mixer input. Howzthat? Well, in order to
be able to re-render the mixer output stream, we need to keep track of the
sounds we've mixed, in order to be able to remix them if the need arises.
That means we need to keep track of a few seconds of source data. This can
get very complicated, for example, imaging playing five 1/4 second sample five
times during a 5 second interval. You not only have to keep track of the
samples you played, but also the amount of time _between_ the samples, in
order to be able to rerender the channel stream accurately. In order to
rewind, the channel keeps a pointer to the current data source position.
So, a mixer channel supports the following methods:
Render -- Renders the individual data sources in the channel
Rewind -- Rewinds the channel's current position pointer
Trim -- Trims the channel to a certain length (from downtimewards)
Truncate -- Truncates the channel at the current position
Add -- Adds a new data source and the end of the channel
So, a channel is a higher level abstraction of a data source -- a
heterogeneous, temporally ordered stream
Here's what a typical mixer channel might look like:
+------------- + +---------------+ +--------------+
| Audio Sample |-->| Silence |-->| AudioSample |-->0
0<--| (dsrcSample) |<--| (dsrcSilence) |<--| (dsrcSample) |
+--------------+ +---------------+ +--------------+
^
CUR----+
MIXING RING (Hungarian notation: mr)
The mixing ring is where the actual sound mixing takes place. At this
level of abstraction, we have an array of mixer channels as our input. Each
mixer channel provides data for us in the output data format. But, we've got
to mix a certain number of samples ahead, in order to ensure that enough
data is queued in the hardware abstraction layer. So, when a new sound is
added with a mixPlay call, we have to do six things:
- Determine how many samples have been mixed but haven't been
played through the speakers yet.
- Rewind every input channel by that amount
- Truncate the channel we're adding too
- Add the new sound to that channel
- Reset the sound output hardware
- Fill the sound output buffer with remixed samples
This is not a trivial task. The worst part is rewinding and remixing all
the input channels. The mixing ring reduces the overhead of rerender with
each remix.
The mixing ring is an array of ring buffers. Each ring buffer corresponds
to one input channel, and holds all the samples that have been rendered for
the last few seconds from that channel. With the mixing ring, when a new
sound is mixed in, we only have to rerender the channel that has changed,
the other channels are stored in the ring buffer. The ring buffer has a
start, an end, and a current position. Since all the ring buffers always
have the same number of samples and current position offset, only one set
of control pointers is needed. The mixing ring supports the following
methods:
RenderAll - Ensure that all channels have been rendered
ReRender - Rewind and rerender a ring buffer
Mix - Mix all ring buffers and store output stream
NewSound - Add a new sound to a ring buffer channel
HARDWARE ABSTRACTION LAYER (Hungarian notation: wave)
Finally, mixed sound samples leave the mixing ring layer and are ready to
be send to the sound hardware. Everything before this point could be
implemented portably and generically, but not this. This is a platform
specific task, and I've tried to come up with a simple, flexible API to
describe this task. The wave API implements a blocked ring buffer in
which blocks of samples are queued to be send to the sound hardware.
rescode waveOpen(waveRate, waveBits, waveSpeak, int*, int*, int*);
Opens the sound hardware at the requested rate, bits, and speakers.
This function returns the following referenced values:
blocksize - size of a wave buffer block, in samples
playahead - number of blocks the mixer should keep in
output ring
immediateremix - flag: recommended to remix audio data?
rescode waveClose(void)
Closes the sound hardware, stopping all noise.
rescode waveReset(void);
Does NOT reset the sound hardware. This sets a flag indicating whether
the next wavePlay call should empty the output buffer before playing
the indicated block.
rescode waveGetPlayPtr(pmem *);
Gets a pointer to an unused audio block, so that it can be filled. You
must have one wavePlay call for every waveGetPlayPtr call.
rescode wavePlay(void);
Adds the block returned from the last waveGetPlayPtr call to the end
of the output ring buffer. However, if waveReset was called before
calling this, the output buffer is emptied and the block should
start playing immediately.
rescode waveGetSampleCount(long *)
This returns the total number of samples that are queued for output,
but haven't gone through the speakers yet. This count should be
accurate to the sample, if possible.
rescode waveSetVolume(short);
Sets the rough output volume of the sound hardware. The volume can
range from 0 (silent) to 7 (full). This function is not called from
the mixer, but should be called from the application to set a user-
controlled output level. The advantage of this over the mixer
volume scaling is that this is an analog scale -- you can reduce
volume without losing bits of sample resolution.
rescode waveGetVolume(short *);
Get the current hardware volume setting. 0 is silence, 7 is
full volume.
MIXER API
The mixer API is a set of function that provide a simple, consistent
programmer interface to the different mixer objects. They hide the
complexity of the mixer from the application layer. See the section
"mixer api" for more information.